package com.vip.mvc.dispacher;


import akka.actor.ActorSystem;
import com.vip.mvc.annotation.Async;
import com.vip.mvc.netty.NettyHttpRequestParam;
import com.vip.mvc.utils.JSONUtils;
import com.vip.mvc.utils.PackageUtil;
import com.vip.mvc.utils.reflection.ClassReflection;
import com.vip.mvc.utils.reflection.ReflectionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created by cloudy.xie on 17/1/16.
 */
public class MyDispacher {
    private static org.slf4j.Logger logger = LoggerFactory.getLogger(MyDispacher.class);

    private MyDispacher() {
    }

    private static MyDispacher instance = new MyDispacher();

    public static MyDispacher getInstance() {
        return instance;
    }

    private final int MAX_TIME_OUT = 30;
    private final Duration TIME_OUT = Duration.create(MAX_TIME_OUT, TimeUnit.SECONDS);

    private ActorSystem actorSystem = ActorSystem.apply("controller-system");

    public void init() {
        RequestMap.getInstance().initRequestMap();
    }

    public void doDispatch(ChannelHandlerContext ctx, NettyHttpRequestParam request) throws Exception {
        if (StringUtils.isBlank(request.getUrl())) {
            logger.error("requestmaping 不能配置为空");
            return;
        }

        if (!RequestMap.getInstance().checkIsExist(request.getUrl())) {
            logger.error("url 错误,请求url:" + request.getUrl() + " 没有配置");
            return;
        }

        RequestParam requestMap = RequestMap.getInstance().getRequestParam(request.getUrl());
        Object methodResp = ReflectionUtils.invokeMethod(requestMap.getMethod(), requestMap.getObject(), request);
        if (requestMap.getMethod().isAnnotationPresent(Async.class)) {
            Future<Object> future = (Future<Object>) methodResp;
            Object respObj = Await.result(future, TIME_OUT);
            writeHttpResponse(ctx, respObj);
        } else {
            writeHttpResponse(ctx, methodResp);
        }
    }

    /**
     * 写到客户端
     */
    private void writeHttpResponse(ChannelHandlerContext ctx, Object responseObj) throws Exception {
        String respJson = JSONUtils.toJSON(responseObj);
        writeHttpByteBuf(ctx, Unpooled.copiedBuffer(respJson, CharsetUtil.UTF_8));
    }

    private void writeHttpByteBuf(ChannelHandlerContext ctx, ByteBuf content) {
        if (ctx.channel().isWritable()) {
            FullHttpResponse msg;
            if (content != null) {
                msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
                msg.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain;charset=UTF-8");
            } else {
                msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
            }

            if (msg.content() != null)
                msg.headers().set(HttpHeaders.Names.CONTENT_LENGTH, msg.content().readableBytes());

            // not keep-alive
            ctx.write(msg).addListener(ChannelFutureListener.CLOSE);
        }
    }

    public ActorSystem getActorSystem() {
        return actorSystem;
    }
}
